home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 001 / pibt40s5.arc / SENDXMOD.MOD < prev    next >
Text File  |  1987-09-09  |  48KB  |  1,253 lines

  1. (*----------------------------------------------------------------------*)
  2. (*                Send_Xmodem_File --- Upload file using XMODEM         *)
  3. (*----------------------------------------------------------------------*)
  4.  
  5. PROCEDURE Send_Xmodem_File( Use_CRC : BOOLEAN );
  6.  
  7. (*----------------------------------------------------------------------*)
  8. (*                                                                      *)
  9. (*     Procedure:  Send_Xmodem_File                                     *)
  10. (*                                                                      *)
  11. (*     Purpose:    Uploads file to remote host using XMODEM protocol.   *)
  12. (*                                                                      *)
  13. (*     Calling Sequence:                                                *)
  14. (*                                                                      *)
  15. (*        Send_Xmodem_File( Use_CRC );                                  *)
  16. (*                                                                      *)
  17. (*           Use_CRC --- TRUE to use Cyclic redundancy check version    *)
  18. (*                       of XMODEM; FALSE to use Checksum version.      *)
  19. (*                                                                      *)
  20. (*     Remarks:                                                         *)
  21. (*                                                                      *)
  22. (*        The file's existence should have been already checked         *)
  23. (*        prior to calling this routine.                                *)
  24. (*                                                                      *)
  25. (*        The transmission parameters are automatically set to:         *)
  26. (*                                                                      *)
  27. (*               Current baud rate, 8 bits, No parity, 1 stop           *)
  28. (*                                                                      *)
  29. (*        and then they are automatically restored to the previous      *)
  30. (*        values when the transfer is complete.                         *)
  31. (*                                                                      *)
  32. (*     Calls:    KeyPressed                                             *)
  33. (*               Async_Send                                             *)
  34. (*               Async_Receive                                          *)
  35. (*               Compute_Crc                                            *)
  36. (*               Draw_Menu_Frame                                        *)
  37. (*               Save_Screen                                            *)
  38. (*               Restore_Screen                                         *)
  39. (*               Async_Open                                             *)
  40. (*                                                                      *)
  41. (*----------------------------------------------------------------------*)
  42.  
  43.                                    (* If this threshhold value x number *)
  44.                                    (* of bad blocks > number of good    *)
  45.                                    (* blocks, reduce block size to 128  *)
  46. CONST
  47.    Bad_Threshhold  = 6;
  48.    SOH_Tries       = 5;
  49.    NAK_Ch          = ^U;
  50.    WXmodem_Window  = 4;
  51.    SeaLink_Window  = 6;
  52.  
  53. VAR
  54.    Xfile_Byte      : FILE OF BYTE  (* Same as transfer file, file size  *);
  55.    XFile_Handle    : INTEGER       (* File handle for file to transfer  *);
  56.    XFile_Name      : AnyStr        (* Full file name including path     *);
  57.    I               : INTEGER       (* Loop index                        *);
  58.    L               : INTEGER       (* General length                    *);
  59.    Tries           : INTEGER       (* # of tries sending current sector *);
  60.    Checksum        : INTEGER       (* Sector checksum                   *);
  61.    Crc             : INTEGER       (* Cyclic redundancy check           *);
  62.    Ch              : INTEGER       (* Character received from COM port  *);
  63.    Kbd_Ch          : CHAR          (* Absorbs keyboard characters       *);
  64.    Send_Errors     : INTEGER       (* Counts transfer errors            *);
  65.    Blocks_To_Send  : INTEGER       (* Number of blocks to send          *);
  66.    Sector_Count    : INTEGER       (* Sector count -- no wrap at 255    *);
  67.    Transfer_Time   : INTEGER       (* Transfer time in seconds          *);
  68.    Starting_Time   : INTEGER       (* Starting transfer time            *);
  69.    Trans_Hours     : INTEGER       (* Transfer time -- hours component  *);
  70.    Trans_Minutes   : INTEGER       (* Transfer time -- mins. component  *);
  71.    Trans_Seconds   : INTEGER       (* Transfer time -- secs. component  *);
  72.    S_Hours         : STRING[2]     (* Hours in character form           *);
  73.    S_Minutes       : STRING[2]     (* Minutes in character form         *);
  74.    S_Seconds       : STRING[2]     (* Seconds in character form         *);
  75.    Time_To_Send    : REAL          (* Time in seconds to transfer file  *);
  76.    Time_Per_Blk    : REAL          (* Time in seconds to transfer block *);
  77.    Effective_Rate  : REAL          (* Effective baud rate of transfer   *);
  78.    Start_Time      : REAL          (* Starting time of transfer         *);
  79.    End_Time        : REAL          (* Ending time of transfer           *);
  80.    NRead           : INTEGER       (* Records actually read from file   *);
  81.    EOF_Xfile       : BOOLEAN       (* EOF encountered on file to send   *);
  82.    Tname           : STRING[20]    (* Transfer type                     *);
  83.    Alt_S_Found     : BOOLEAN       (* TRUE if alt_s entered             *);
  84.    Max_Tries       : INTEGER       (* Max. number of retries            *);
  85.    R_File_Size     : REAL          (* File size as real                 *);
  86.    Header_Ch       : CHAR          (* Block header character            *);
  87.    New_Header_Ch   : CHAR          (* Revised block header if downshift *);
  88.    Bad_Sectors     : INTEGER       (* Count of bad sectors              *);
  89.    Good_Sectors    : INTEGER       (* Count of good sectors             *);
  90.    ITime           : INTEGER       (* Counter for wait loops            *);
  91.    XFile_Size      : REAL          (* File size in characters           *);
  92.    Do_ACKs         : BOOLEAN       (* TRUE to do ACKs                   *);
  93.    Save_XonXoff    : BOOLEAN       (* Saves XON/XOFF status             *);
  94.    ACK_Window      : INTEGER       (* ACK window size for WXModem       *);
  95.    ACK_Sector      : INTEGER       (* Sector # ACK'd or NAK'd           *);
  96.    Max_ACK_Window  : INTEGER       (* Max # of sectors in window        *);
  97.    Max_Window_Size : INTEGER       (* Max window size                   *);
  98.    Max_Window_Size1: INTEGER       (* Max window size + 1               *);
  99.    Sending_Title   : AnyStr        (* Title for send                    *);
  100.    Err_Mess        : AnyStr        (* Error message text                *);
  101.  
  102. LABEL 1;
  103.  
  104. (*----------------------------------------------------------------------*)
  105. (*        Initialize_Send_Display --- initialize send status display    *)
  106. (*----------------------------------------------------------------------*)
  107.  
  108. PROCEDURE Initialize_Send_Display;
  109.  
  110. BEGIN (* Initialize_Send_Display *)
  111.  
  112.    TextColor( Menu_Text_Color_2 );
  113.    WRITE  (' Blocks to send        : ');
  114.    TextColor( Menu_Text_Color   );
  115.    WRITE  ( Blocks_To_Send );
  116.    GoToXY( 35 , WhereY );
  117.    WRITE  ( Blocks_To_Send SHR 3, 'K' );
  118.    WRITELN;
  119.  
  120.    TextColor( Menu_Text_Color_2 );
  121.    WRITE  (' Approx. transfer time : ');
  122.    TextColor( Menu_Text_Color );
  123.    WRITELN( TimeString( Time_To_Send , Military_Time ) );
  124.    WRITELN(' ');
  125.  
  126.    TextColor( Menu_Text_Color_2 );
  127.    WRITELN(' Sending block         : ');
  128.    WRITELN(' Errors                : ');
  129.    WRITE  (' Time remaining        : ');
  130.  
  131.    TextColor( Menu_Text_Color );
  132.    WRITELN(TimeString( Time_To_Send , Military_Time ) );
  133.    WRITELN(' ');
  134.  
  135.    TextColor( Menu_Text_Color_2 );
  136.    WRITE  (' Last status message   : ');
  137.  
  138.    TextColor( Menu_Text_Color );
  139.  
  140. END   (* Initialize_Send_Display *);
  141.  
  142. (*----------------------------------------------------------------------*)
  143. (*        Flip_Display_Status --- turn status display on/off            *)
  144. (*----------------------------------------------------------------------*)
  145.  
  146. PROCEDURE Flip_Display_Status;
  147.  
  148. BEGIN (* Flip_Display_Status *)
  149.  
  150.    CASE Display_Status OF
  151.  
  152.       TRUE:   BEGIN
  153.                                    (* Indicate no display   *)
  154.  
  155.                  Display_Status := FALSE;
  156.  
  157.                                    (* Remove XMODEM window  *)
  158.  
  159.                  Restore_Screen( Saved_Screen );
  160.  
  161.                                    (* Remove batch transfer screen *)
  162.                                    (* if any.                      *)
  163.  
  164.                  Restore_Screen( Batch_Screen_Ptr );
  165.  
  166.                  Reset_Global_Colors;
  167.  
  168.                                    (* Restore cursor *)
  169.                  CursorOn;
  170.  
  171.               END;
  172.  
  173.       FALSE:  BEGIN
  174.                                    (* Indicate display will be done *)
  175.  
  176.                  Display_Status := TRUE;
  177.  
  178.                                    (* Hide cursor *)
  179.                  CursorOff;
  180.  
  181.                                    (* Initialize batch transfer display *)
  182.                                    (* if needed.                        *)
  183.  
  184.                  IF ( NOT Single_File_Protocol[Transfer_Protocol] ) THEN
  185.                     Display_Batch_Window;
  186.  
  187.                                    (* Save screen image *)
  188.  
  189.                  Save_Screen( Saved_Screen );
  190.  
  191.                                    (* Initialize Xmodem display window   *)
  192.  
  193.                  Draw_Menu_Frame( 15, 10, 78, 19, Menu_Frame_Color,
  194.                                   Menu_Title_Color, Menu_Text_Color,
  195.                                   'Send file ' + FileName + ' using ' + Tname );
  196.  
  197.                                    (* Headings for status information *)
  198.  
  199.                  Window( 16, 11, 77, 18 );
  200.  
  201.                                    (* Set up titles *)
  202.  
  203.                  Initialize_Send_Display;
  204.  
  205.               END;
  206.  
  207.    END (* CASE *);
  208.  
  209. END   (* Flip_Display_Status *);
  210.  
  211. (*----------------------------------------------------------------------*)
  212. (*   Update_Xmodem_Send_Display --- Update display of Xmodem sending    *)
  213. (*----------------------------------------------------------------------*)
  214.  
  215. PROCEDURE Update_Xmodem_Send_Display;
  216.  
  217. BEGIN (* Update_Xmodem_Send_Display *)
  218.  
  219.    GoToXY( 26 , 4 );
  220.    WRITE( Sector_Count );
  221.    GoToXY( 35 , 4 );
  222.    WRITE( Sector_Count SHR 3, 'K' );
  223.    GoToXY( 26 , 5 );
  224.    WRITE( Send_Errors  );
  225.  
  226. END   (* Update_Xmodem_Send_Display *);
  227.  
  228. (*----------------------------------------------------------------------*)
  229. (*         Display_Send_Error --- Display XMODEM sending error          *)
  230. (*----------------------------------------------------------------------*)
  231.  
  232. PROCEDURE Display_Send_Error( Err_Text: AnyStr; Display_Block: BOOLEAN );
  233.  
  234. VAR
  235.    S: STRING[10];
  236.    I: INTEGER;
  237.  
  238. BEGIN (* Display_Send_Error *)
  239.  
  240.    IF ( NOT Display_Status ) THEN
  241.       Flip_Display_Status;
  242.  
  243.    Err_Mess := Err_Text;
  244.  
  245.    IF Display_Block THEN
  246.       BEGIN
  247.          I := MAX( Sector_Count - 1 , 0 );
  248.          STR( I , S );
  249.          Err_Mess := Err_Mess + ' at/before block ' + S;
  250.       END;
  251.  
  252.    GoToXY( 26 , 8 );
  253.    WRITE(Err_Mess);
  254.    ClrEol;
  255.  
  256.    Write_Log( Err_Mess, TRUE, FALSE );
  257.  
  258. END   (* Display_Send_Error *);
  259.  
  260. (*----------------------------------------------------------------------*)
  261. (*               Check_Keyboard --- Check for keyboard entry            *)
  262. (*----------------------------------------------------------------------*)
  263.  
  264. PROCEDURE Check_Keyboard;
  265.  
  266. BEGIN (* Check_Keyboard *)
  267.  
  268.    WHILE KeyPressed DO
  269.       BEGIN
  270.          READ( Kbd, Kbd_Ch );
  271.          IF ( Kbd_Ch = CHR( ESC ) ) AND KeyPressed THEN
  272.             BEGIN
  273.                READ( Kbd , Kbd_Ch );
  274.                CASE ORD( Kbd_Ch ) OF
  275.                   Alt_S     : Alt_S_Found := TRUE;
  276.                   Shift_Tab : Flip_Display_Status;
  277.                   ELSE        Handle_Function_Key( Kbd_Ch );
  278.                END (* CASE *);
  279.                Stop_Send   := Stop_Send OR Alt_S_Found;
  280.             END;
  281.       END;
  282.  
  283. END   (* Check_Keyboard *);
  284.  
  285. (*----------------------------------------------------------------------*)
  286. (*        Xmodem_Wait_For_Ch --- wait for character to appear           *)
  287. (*----------------------------------------------------------------------*)
  288.  
  289. PROCEDURE Xmodem_Wait_For_Ch(     Wait_Time: INTEGER;
  290.                               VAR Ch       : INTEGER );
  291.  
  292. VAR
  293.    ITime : INTEGER;
  294.  
  295. BEGIN (* Xmodem_Wait_For_Ch *)
  296.  
  297.    ITime := 0;
  298.  
  299.    REPEAT
  300.       ITime := SUCC( ITime );
  301.       Async_Receive_With_Timeout( One_Second , Ch );
  302.       Check_KeyBoard;
  303.    UNTIL ( Ch <> TimeOut ) OR ( ITime >= Wait_Time ) OR Stop_Send;
  304.  
  305. END   (* Xmodem_Wait_For_Ch *);
  306.  
  307. (*----------------------------------------------------------------------*)
  308. (*           Do_Initial_Handshake --- Do initial C/G/NAK handshaking    *)
  309. (*----------------------------------------------------------------------*)
  310.  
  311. PROCEDURE Do_Initial_Handshake;
  312.  
  313. BEGIN (* Do_Initial_Handshake *)
  314.                                    (* Get initial character             *)
  315.    GoToXY( 26 , 8 );
  316.    WRITE('Wait for NAK/C/G/W --- ');
  317.    ClrEol;
  318.                                    (* Set window size *)
  319.    Max_Window_Size  := 0;
  320.    Max_Window_Size1 := 1;
  321.                                    (* Look for NAK/C/G/W *)
  322.    REPEAT
  323.  
  324.       Xmodem_Wait_For_Ch( Xmodem_Block_Wait , Ch );
  325.  
  326.                                    (* If CAN, insist on another *)
  327.       IF Ch = CAN THEN
  328.          Xmodem_Wait_For_Ch( Xmodem_ACK_Wait , Ch );
  329.  
  330.       Tries := SUCC( Tries );
  331.  
  332.       Stop_Send := Stop_Send OR ( NOT Async_Carrier_Detect );
  333.  
  334.    UNTIL ( Tries > SOH_Tries  ) OR
  335.          ( Ch    = NAK        ) OR
  336.          ( Ch    = ORD( 'C' ) ) OR
  337.          ( Ch    = ORD( 'G' ) ) OR
  338.          ( Ch    = ORD( 'W' ) ) OR
  339.          ( Ch    = CAN        ) OR
  340.          Stop_Send;
  341.  
  342.    IF ( Ch    = TimeOut   ) OR
  343.       ( Tries > SOH_Tries ) OR
  344.       ( Ch    = CAN       ) THEN
  345.       BEGIN
  346.          IF ( NOT Display_Status ) THEN
  347.             Flip_Display_Status;
  348.          GoToXY( 26 , 51 );
  349.          WRITE('Not Received ');
  350.          ClrEol;
  351.          Stop_Send := TRUE;
  352.       END
  353.    ELSE IF ( Ch = NAK ) THEN
  354.       Use_Crc := FALSE
  355.    ELSE IF ( Ch = ORD( 'C' ) ) THEN
  356.       Use_Crc := TRUE
  357.    ELSE IF ( Ch = ORD( 'G' ) ) THEN
  358.       BEGIN
  359.          Use_Crc          := TRUE;
  360.          Do_ACKs          := FALSE;
  361.          Async_Do_XonXoff := TRUE;
  362.       END
  363.    ELSE IF ( Ch = ORD( 'W' ) ) THEN
  364.       BEGIN
  365.          Use_Crc          := TRUE;
  366.          Do_WXModem       := TRUE;
  367.          Async_Do_XonXoff := TRUE;
  368.          Max_ACK_Window   := WXmodem_Window;
  369.          Max_Window_Size  := WXmodem_Window;
  370.          Max_Window_Size1 := SUCC( Max_Window_Size );
  371.       END;
  372.                                    (* Indicate OK reception             *)
  373.    IF ( NOT Stop_Send ) THEN
  374.       BEGIN
  375.  
  376.          IF ( NOT Display_Status ) THEN
  377.             Flip_Display_Status;
  378.  
  379.          GoToXY( 26 , 51 );
  380.  
  381.          WRITE('Received ');
  382.  
  383.          CASE CHR( Ch ) OF
  384.             'C','G','W' : WRITE( CHR( Ch ) );
  385.             NAK_Ch      : WRITE('NAK');
  386.             ELSE;
  387.          END (* CASE *);
  388.  
  389.          ClrEol;
  390.  
  391.       END;
  392.                                    (* Reset status line *)
  393.    IF Do_Status_Line THEN
  394.       BEGIN
  395.          Set_Status_Line_Name( Short_Terminal_Name );
  396.          Write_To_Status_Line( Status_Line_Name, 1 );
  397.       END;
  398.  
  399. END   (* Do_Initial_Handshake *);
  400.  
  401. (*----------------------------------------------------------------------*)
  402. (*      Async_Send_DLE_Char --- Send possibly DLE-quoted character      *)
  403. (*----------------------------------------------------------------------*)
  404.  
  405. PROCEDURE Async_Send_DLE_Char( C: CHAR );
  406.  
  407. BEGIN (* Async_Send_DLE_Char *)
  408.  
  409. INLINE(
  410.   $8A/$46/<C              {Dle1:    MOV     AL,[BP+<C]                 ;Pick up character to send}
  411.   /$F6/$06/>Do_WXMODEM/$01{         TEST    BYTE [<Do_WXmodem],1       ;Check if we're doing WXmodem}
  412.   /$74/$21                {         JZ      Dle3                       ;No -- just ordinary send}
  413.                           {;}
  414.   /$3C/<XON               {         CMP     AL,<XON                    ;Else check for chars to be DLE'd: XON}
  415.   /$74/$0E                {         JE      Dle2}
  416.   /$3C/<XOFF              {         CMP     AL,<XOFF                   ;... XOFF}
  417.   /$74/$0A                {         JE      Dle2}
  418.   /$3C/<DLE               {         CMP     AL,<DLE                    ;... DLE}
  419.   /$74/$06                {         JE      Dle2}
  420.   /$3C/<SYN               {         CMP     AL,<SYN                    ;... SYN}
  421.   /$74/$02                {         JE      Dle2}
  422.                           {;}
  423.   /$EB/$0F                {         JMP     SHORT Dle3                 ;Otherwise, treat as normal char}
  424.                           {;}
  425.   /$B8/>DLE               {Dle2:    MOV     AX,>DLE                    ;Push DLE onto stack}
  426.   /$50                    {         PUSH    AX}
  427.   /$8D/$1E/>ASYNC_SEND    {         LEA     BX,[>Async_Send]           ;Call output routine}
  428.   /$FF/$D3                {         CALL    BX                         ;}
  429.                           {;}
  430.   /$8A/$46/<C             {         MOV     AL,[BP+<C]                 ;Pick up character to send again}
  431.   /$34/$40                {         XOR     AL,64                      ;Convert XON/XOFF/SYN/DLE to printable}
  432.                           {;}
  433.   /$50                    {Dle3:    PUSH    AX                         ;Push character to send onto stack}
  434.   /$8D/$1E/>ASYNC_SEND    {         LEA     BX,[>Async_Send]           ;Call output routine}
  435.   /$FF/$D3                {         CALL    BX                         ;}
  436. );
  437.  
  438. END   (* Async_Send_DLE_Char *);
  439.  
  440. (*----------------------------------------------------------------------*)
  441. (*               Handle_Sector_ACK --- Handle ACK/NAK for sectors       *)
  442. (*----------------------------------------------------------------------*)
  443.  
  444. PROCEDURE Handle_Sector_ACKNAK( VAR Ch: INTEGER );
  445.  
  446. VAR
  447.    ACK_Ch     : INTEGER;
  448.    Comp_Ch    : CHAR;
  449.  
  450. BEGIN (* Handle_Sector_ACKNAK *)
  451.  
  452.                                    (* Assume an ACK by default.            *)
  453.    Ch := ACK;
  454.                                    (* If sliding windows, we don't need to *)
  455.                                    (* wait here until send window is full. *)
  456.  
  457.    IF ( Do_WXModem OR Do_SeaLink ) THEN
  458.       IF ( ACK_Window < Max_ACK_Window ) THEN
  459.          IF ( Async_Buffer_Head = Async_Buffer_Tail ) THEN
  460.             EXIT;
  461.                                    (* Pick up a character -- should be ACK *)
  462.  
  463.    Xmodem_Wait_For_Ch( Xmodem_Ack_Wait , Ch );
  464.  
  465.                                    (* If CAN, insist on another *)
  466.    IF ( Ch = CAN ) THEN
  467.       BEGIN
  468.          Xmodem_Wait_For_Ch( Xmodem_Ack_Wait , Ch );
  469.          IF ( Ch = CAN ) THEN EXIT;
  470.       END;
  471.                                    (* If sliding windows, pick up sector   *)
  472.                                    (* for which ACK/NAK applies.  Adjust   *)
  473.                                    (* ACK_Window to reflect sectors not    *)
  474.                                    (* ACK'd yet.                           *)
  475.  
  476.    ACK_Sector := Sector_Number;
  477.  
  478.    IF ( Do_WXModem OR Do_SeaLink ) THEN
  479.       BEGIN
  480.          IF ( ( Ch = ACK ) OR ( Ch = NAK ) ) THEN
  481.             BEGIN
  482.  
  483.                XModem_Wait_For_Ch( XModem_Ack_Wait , ACK_Ch );
  484.  
  485.                IF Do_WXModem THEN
  486.                   IF ( ACK_Ch > PRED( Max_Window_Size ) ) THEN
  487.                      Ch := ACK
  488.                   ELSE
  489.                      BEGIN
  490.                         ACK_Sector := ( ACK_Ch AND 3 );
  491.                         ACK_Window := ( Sector_Number AND 3 ) - ACK_Sector;
  492.                         IF ( ACK_Window < 0 ) THEN
  493.                            ACK_Window := ACK_Window + Max_Window_Size;
  494.                      END
  495.  
  496.                ELSE IF Do_SeaLink THEN
  497.                   BEGIN
  498.                      IF Async_Receive( Comp_Ch ) THEN
  499.                         IF ( ( ORD( Comp_Ch ) + ACK_Ch ) = 255 ) THEN
  500.                            BEGIN
  501.                               ACK_Sector := ( ACK_Ch MOD Max_Window_Size1 );
  502.                               ACK_Window := ( Sector_Number MOD Max_Window_Size1 ) - ACK_Sector;
  503.                               IF ( ACK_Window < 0 ) THEN
  504.                                  ACK_Window := ACK_Window + Max_Window_Size;
  505.                            END
  506.                         ELSE
  507.                            Max_ACK_Window := 0
  508.                      ELSE
  509.                         Max_ACK_Window := 0;
  510.                   END  (* IF SeaLink *);
  511.  
  512.             END (* IF Ach = ACK or BAK *);
  513.  
  514.       END (* IF sliding windows *);
  515.  
  516.                                    (* Display message about NAK *)
  517.    IF ( Ch <> ACK ) THEN
  518.       BEGIN
  519.          Display_Send_Error('No ACK', TRUE);
  520.          Send_Errors := SUCC( Send_Errors );
  521.       END;
  522.  
  523. END   (* Handle_Sector_ACKNAK *);
  524.  
  525. (*----------------------------------------------------------------------*)
  526. (*               Send_Xmodem_Block --- send out Xmodem block            *)
  527. (*----------------------------------------------------------------------*)
  528.  
  529. PROCEDURE Send_Xmodem_Block;
  530.  
  531. VAR
  532.    I          : INTEGER;
  533.    Send_State : INTEGER;
  534.  
  535. BEGIN (* Send_Xmodem_Block *)
  536.                                    (* Reset error count to zero *)
  537.    Tries := 0;
  538.                                    (* Set sending state.  States depend on: *)
  539.                                    (*                                       *)
  540.                                    (*    CRC vs CheckSum                    *)
  541.                                    (*    If Spooling on/off                 *)
  542.                                    (*    If resending or not                *)
  543.    IF Use_CRC THEN
  544.       Send_State := 1
  545.    ELSE
  546.       Send_State := 0;
  547.  
  548.    IF Print_Spooling THEN
  549.       Send_State := Send_State + 2;
  550.  
  551.    REPEAT
  552.                                    (* Send SYN if doing WXModem *)
  553.       IF Do_WXModem THEN
  554.          Async_Send( CHR( SYN ) );
  555.  
  556.                                    (* Send 1st char of block *)
  557.  
  558.       Async_Send_DLE_Char( Header_Ch );
  559.  
  560.                                    (* Send block number and complement *)
  561.  
  562.       Async_Send_DLE_Char( CHR(       Sector_Number ) );
  563.       Async_Send_DLE_Char( CHR( 255 - Sector_Number ) );
  564.  
  565.                                    (* Transmit Sector Data *)
  566.       CASE Send_State OF
  567.  
  568.          0:  BEGIN
  569.                 CheckSum := 0;
  570.                 FOR I := 1 TO Sector_Size DO
  571.                    BEGIN
  572.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  573.                       CheckSum := ( CheckSum + Sector_Data[ I ] ) AND $FF;
  574.                    END;
  575.                 Async_Send_DLE_Char( CHR( CheckSum ) );
  576.              END;
  577.  
  578.          1:  BEGIN
  579.                 Crc := 0;
  580.                 FOR I := 1 TO Sector_Size DO
  581.                    BEGIN
  582.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  583.                       Crc := SWAP( Crc ) XOR Sector_Data[I];
  584.                       Crc := Crc XOR ( LO( Crc ) SHR 4 );
  585.                       Crc := Crc XOR ( SWAP( LO( Crc ) ) SHL 4 ) XOR
  586.                               ( LO( Crc ) SHL 5 );
  587.                    END;
  588.                 Async_Send_DLE_Char( CHR( HI( CRC ) ) );
  589.                 Async_Send_DLE_Char( CHR( LO( CRC ) ) );
  590.              END;
  591.  
  592.          2:  BEGIN
  593.                 CheckSum := 0;
  594.                 FOR I := 1 TO Sector_Size DO
  595.                    BEGIN
  596.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  597.                       CheckSum := ( CheckSum + Sector_Data[ I ] ) AND $FF;
  598.                       IF Print_Spooling THEN
  599.                          Print_Spooled_File;
  600.                    END;
  601.                 Async_Send_DLE_Char( CHR( CheckSum ) );
  602.              END;
  603.  
  604.          3:  BEGIN
  605.                 Crc := 0;
  606.                 FOR I := 1 TO Sector_Size DO
  607.                    BEGIN
  608.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  609.                       Crc := SWAP( Crc ) XOR Sector_Data[I];
  610.                       Crc := Crc XOR ( LO( Crc ) SHR 4 );
  611.                       Crc := Crc XOR ( SWAP( LO( Crc ) ) SHL 4 ) XOR
  612.                               ( LO( Crc ) SHL 5 );
  613.                       IF Print_Spooling THEN
  614.                          Print_Spooled_File;
  615.                    END;
  616.                 Async_Send_DLE_Char( CHR( HI( CRC ) ) );
  617.                 Async_Send_DLE_Char( CHR( LO( CRC ) ) );
  618.              END;
  619.  
  620.          4:  BEGIN
  621.                 FOR I := 1 TO Sector_Size DO
  622.                    Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  623.                 Async_Send_DLE_Char( CHR( CheckSum ) );
  624.              END;
  625.  
  626.          5:  BEGIN
  627.                 FOR I := 1 TO Sector_Size DO
  628.                    Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  629.                 Async_Send_DLE_Char( CHR( HI( CRC ) ) );
  630.                 Async_Send_DLE_Char( CHR( LO( CRC ) ) );
  631.              END;
  632.  
  633.          6:  BEGIN
  634.                 FOR I := 1 TO Sector_Size DO
  635.                    BEGIN
  636.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  637.                       IF Print_Spooling THEN
  638.                          Print_Spooled_File;
  639.                    END;
  640.                 Async_Send_DLE_Char( CHR( CheckSum ) );
  641.              END;
  642.  
  643.          7:  BEGIN
  644.                 FOR I := 1 TO Sector_Size DO
  645.                    BEGIN
  646.                       Async_Send_DLE_Char( CHR( Sector_Data[ I ] ) );
  647.                       IF Print_Spooling THEN
  648.                          Print_Spooled_File;
  649.                    END;
  650.                 Async_Send_DLE_Char( CHR( HI( CRC ) ) );
  651.                 Async_Send_DLE_Char( CHR( LO( CRC ) ) );
  652.              END;
  653.  
  654.       END (* CASE *);
  655.                                    (* Purge receive buffer *)
  656.  
  657.       IF ( NOT ( Do_WXModem OR Do_SEALink OR ( NOT Do_Acks ) ) ) THEN
  658.          Async_Purge_Buffer;
  659.                                    (* Not first time through anymore *)
  660.  
  661.       Send_State := Send_State OR 4;
  662.  
  663.                                    (* Increment count of tries to send  *)
  664.                                    (* for this sector.                  *)
  665.       Tries := SUCC( Tries );
  666.                                    (* Handle sector ACK/NAK             *)
  667.       IF Do_Acks THEN
  668.          Handle_Sector_ACKNAK( Ch )
  669.       ELSE
  670.          Ch := ACK;
  671.                                    (* Update display *)
  672.       IF Display_Status THEN
  673.          Update_Xmodem_Send_Display;
  674.  
  675.    UNTIL ( Ch = ACK           ) OR
  676.          ( Ch = CAN           ) OR
  677.          ( Tries > Max_Tries  ) OR
  678.          ( Stop_Send          ) OR
  679.          ( Async_Carrier_Drop ) OR
  680.          ( Do_WXModem         ) OR
  681.          ( Do_SEALink         );
  682.                                    (* Inc WXModem un-ACKd sector count *)
  683.  
  684.    ACK_Window := SUCC( ACK_Window );
  685.  
  686.                                    (* Ensure Stop_Send TRUE if carrier *)
  687.                                    (* dropped.                         *)
  688.  
  689.    Stop_Send := Stop_Send OR Async_Carrier_Drop;
  690.  
  691. END   (* Send_Xmodem_Block *);
  692.  
  693. (*----------------------------------------------------------------------*)
  694. (*      Send_Telink_Header --- send out special block 0 for Telink      *)
  695. (*----------------------------------------------------------------------*)
  696.  
  697. PROCEDURE Send_Telink_Header;
  698.  
  699. VAR
  700.    Save_Size: INTEGER;
  701.    Save_CRC : BOOLEAN;
  702.  
  703. BEGIN (* Send_Telink_Header *)
  704.                                    (* Always send TELINK in Checksum mode *)
  705.    Max_Tries     := 3;
  706.    Save_Size     := Sector_Size;
  707.    Save_CRC      := Use_CRC;
  708.    Sector_Size   := 128;
  709.    Use_CRC       := FALSE;
  710.    Header_Ch     := CHR( SYN );
  711.  
  712.    Send_Xmodem_Block;
  713.  
  714.    Sector_Size   := Save_Size;
  715.    Use_CRC       := Save_CRC;
  716.    Max_Tries     := Xmodem_Max_Errors;
  717.  
  718.    IF Display_Status THEN
  719.       IF ( Ch = ACK ) THEN
  720.          Display_Send_Error( 'Telink header accepted.' , FALSE )
  721.       ELSE
  722.          Display_Send_Error( 'Telink header not accepted.' , FALSE );
  723.  
  724. END   (* Send_Telink_Header *);
  725.  
  726. (*----------------------------------------------------------------------*)
  727. (*      Send_SEAlink_Header --- send out special block 0 for SEAlink    *)
  728. (*----------------------------------------------------------------------*)
  729.  
  730. PROCEDURE Send_SEAlink_Header;
  731.  
  732. VAR
  733.    Save_Size: INTEGER;
  734.  
  735. BEGIN (* Send_SEAlink_Header *)
  736.  
  737.    Max_Tries     := 3;
  738.    Save_Size     := Sector_Size;
  739.    Sector_Size   := 128;
  740.    Header_Ch     := CHR( SOH );
  741.    Use_CRC       := TRUE;
  742.    Do_SEALink    := FALSE;
  743.  
  744.    Send_Xmodem_Block;
  745.  
  746.    Sector_Size   := Save_Size;
  747.    Max_Tries     := Xmodem_Max_Errors;
  748.    Do_SEALink    := TRUE;
  749.  
  750.    IF Display_Status THEN
  751.       IF ( Ch = ACK ) THEN
  752.          Display_Send_Error( 'SEAlink header accepted.' , FALSE )
  753.       ELSE
  754.          Display_Send_Error( 'SEAlink header not accepted.' , FALSE );
  755.  
  756. END   (* Send_SEAlink_Header *);
  757.  
  758. (*----------------------------------------------------------------------*)
  759. (*      Send_Ymodem_Header --- send out special block 0 for Ymodem      *)
  760. (*----------------------------------------------------------------------*)
  761.  
  762. PROCEDURE Send_Ymodem_Header;
  763.  
  764. VAR
  765.    Save_Size: INTEGER;
  766.    Save_ACK : BOOLEAN;
  767.  
  768. BEGIN (* Send_Ymodem_Header *)
  769.                                    (* Always send short block 0 *)
  770.    Max_Tries     := 3;
  771.    Save_Size     := Sector_Size;
  772.    Sector_Size   := 128;
  773.    Header_Ch     := CHR( SOH );
  774.    Save_ACK      := Do_ACKs;
  775.    Do_ACKs       := TRUE;
  776.  
  777.    Send_Xmodem_Block;
  778.  
  779.    Sector_Size := Save_Size;
  780.    Do_ACKs     := Save_ACK;
  781.    Max_Tries   := Xmodem_Max_Errors;
  782.  
  783.    IF Display_Status THEN
  784.       IF ( Ch = ACK ) THEN
  785.          Display_Send_Error( 'Ymodem header accepted.' , FALSE )
  786.       ELSE
  787.          Display_Send_Error( 'Ymodem header not accepted.' , FALSE );
  788.  
  789. END   (* Send_Ymodem_Header *);
  790.  
  791. (*----------------------------------------------------------------------*)
  792. (*                 Cancel_Transfer --- Cancel upload                    *)
  793. (*----------------------------------------------------------------------*)
  794.  
  795. PROCEDURE Cancel_Transfer;
  796.  
  797. BEGIN (* Cancel_Transfer *)
  798.                                    (* Purge reception *)
  799.    Async_Purge_Buffer;
  800.                                    (* Send five cancels, then five *)
  801.                                    (* backspaces.                  *)
  802.    Async_Send( CHR( CAN ) );
  803.    Async_Send( CHR( CAN ) );
  804.    Async_Send( CHR( CAN ) );
  805.    Async_Send( CHR( CAN ) );
  806.    Async_Send( CHR( CAN ) );
  807.  
  808.    Async_Send( CHR( BS  ) );
  809.    Async_Send( CHR( BS  ) );
  810.    Async_Send( CHR( BS  ) );
  811.    Async_Send( CHR( BS  ) );
  812.    Async_Send( CHR( BS  ) );
  813.  
  814. END   (* Cancel_Transfer *);
  815.  
  816. (*----------------------------------------------------------------------*)
  817.  
  818. BEGIN (* Send_Xmodem_File *)
  819.                                    (* Open display window for transfer  *)
  820.    Save_Screen( Saved_Screen );
  821.  
  822.    CASE Transfer_Protocol OF
  823.       Xmodem_Chk   : Tname := 'Xmodem (Checksum)';
  824.       Xmodem_Crc   : Tname := 'Xmodem (CRC)';
  825.       Telink       : Tname := 'Telink';
  826.       Modem7_Chk   : Tname := 'Modem7 (Checksum)';
  827.       Modem7_CRC   : Tname := 'Modem7 (CRC)';
  828.       Xmodem_1K    : Tname := 'Xmodem 1K';
  829.       Xmodem_1KG   : Tname := 'Xmodem 1K G';
  830.       Ymodem_Batch : Tname := 'Ymodem Batch';
  831.       Ymodem_G     : Tname := 'Ymodem G Batch';
  832.       WXmodem      : Tname := 'Windowed Xmodem';
  833.       SeaLink      : Tname := 'SEALink';
  834.    END (* CASE *);
  835.  
  836.    Sending_Title := 'Send file ' + FileName + ' using ' + Tname;
  837.  
  838.    Draw_Menu_Frame( 15, 10, 78, 19, Menu_Frame_Color, Menu_Title_Color,
  839.                     Menu_Text_Color, Sending_Title );
  840.  
  841.                                    (* Headings for status information *)
  842.    Window( 16, 11, 77, 18 );
  843.  
  844.    IF ( POS( '\' , FileName ) = 0 ) AND
  845.       ( POS( ':' , FileName ) = 0 ) THEN
  846.       XFile_Name := Upload_Dir_Path + FileName
  847.    ELSE
  848.       XFile_Name := FileName;
  849.  
  850.    Write_Log( 'Send file ' + XFile_Name + ' using ' + Tname, FALSE, FALSE );
  851.  
  852.    ASSIGN( Xfile_Byte , XFile_Name );
  853.       (*$I-*)
  854.    RESET ( Xfile_Byte );
  855.       (*$I+*)
  856.  
  857.    IF ( Int24Result <> 0 ) THEN
  858.       BEGIN
  859.          WRITE('Cannot open file to send, transfer cancelled.');
  860.          Cancel_Transfer;
  861.          DELAY( One_Second_Delay );
  862.          Restore_Screen( Saved_Screen );
  863.          Reset_Global_Colors;
  864.          EXIT;
  865.       END;
  866.                                    (* Get file size in characters.      *)
  867.  
  868.    XFile_Size  := LongFileSize( Xfile_Byte );
  869.    R_File_Size := XFile_Size;
  870.                                    (* Number of retries of bad block *)
  871.  
  872.    Max_Tries   := Xmodem_Max_Errors;
  873.  
  874.                                    (* Figure approx. time for upload *)
  875.  
  876.    Blocks_To_Send := ROUND( ( Xfile_Size / 128 ) + 0.49 );
  877.    Time_To_Send   := Blocks_To_Send * ( Trans_Time_Val / Baud_Rate );
  878.    Time_Per_Blk   := Time_To_Send / Blocks_To_Send;
  879.  
  880.       (*$I-*)
  881.    CLOSE ( Xfile_Byte );
  882.       (*$I+*)
  883.  
  884.    I := Int24Result;
  885.                                    (* Hide cursor *)
  886.    CursorOff;
  887.                                    (* Headings for status information *)
  888.    Initialize_Send_Display;
  889.                                    (* Determine sector size             *)
  890.                                    (* Note:  If Ymodem and downsizing   *)
  891.                                    (*        allowed, and file is < 1K, *)
  892.                                    (*        use short sectors.         *)
  893.                                    (* Also set header character.        *)
  894.    Header_Ch := CHR( SOH );
  895.  
  896.    IF ( Transfer_Protocol IN [Xmodem_1K, Xmodem_1KG, Ymodem_Batch, Ymodem_G] ) THEN
  897.       BEGIN
  898.          IF ( DownSize_Ymodem AND ( Xfile_Size < 1024.0 ) ) THEN
  899.             BEGIN
  900.                Sector_Size := 128;
  901.                Display_Send_Error('Switching to 128 byte blocks', FALSE);
  902.             END
  903.          ELSE
  904.             BEGIN
  905.                Sector_Size := 1024;
  906.                Header_Ch := CHR( STX );
  907.             END
  908.       END
  909.    ELSE
  910.       Sector_Size := 128;
  911.  
  912.    New_Header_Ch := Header_Ch;
  913.                                    (* Open file to send *)
  914.  
  915.    I := Open_File_Handle( XFile_Name, Access_Read_Mode, XFile_Handle );
  916.  
  917.    IF ( I <> 0 ) OR ( Int24Result <> 0 ) THEN
  918.       BEGIN
  919.          WRITE('Cannot open file to send, transfer cancelled.');
  920.          Cancel_Transfer;
  921.          DELAY( One_Second_Delay );
  922.          Restore_Screen( Saved_Screen );
  923.          Reset_Global_Colors;
  924.          EXIT;
  925.       END;
  926.                                    (* Sector #s start at 1, wrap at 255 *)
  927.    Sector_Number := 0;
  928.    Sector_Count  := 0;
  929.                                    (* No errors yet                     *)
  930.    Send_Errors   := 0;
  931.                                    (* Set TRUE if errors halt transfer  *)
  932.    Stop_Send     := FALSE;
  933.                                    (* Starting time for transfer        *)
  934.    Start_Time    := TimeOfDay;
  935.                                    (* Set EOF on Xfile to FALSE         *)
  936.    EOF_Xfile     := FALSE;
  937.                                    (* Set Alt_S encountered off         *)
  938.    Alt_S_Found   := FALSE;
  939.                                    (* No retries yet                    *)
  940.    Tries         := 0;
  941.                                    (* Assume ACKs                       *)
  942.    Do_ACKs       := TRUE;
  943.                                    (* Assume no windowing to be done    *)
  944.    Do_WXModem    := FALSE;
  945.    Do_SeaLink    := FALSE;
  946.    ACK_Window    := 0;
  947.    Max_ACK_Window:= 0;
  948.                                    (* Set up for SeaLink                *)
  949.  
  950.    IF ( Transfer_Protocol = SeaLink ) THEN
  951.       BEGIN
  952.          Do_SeaLink       := TRUE;
  953.          Max_Window_Size  := 6;
  954.          Max_Window_Size1 := 7;
  955.          Max_ACK_Window   := Max_Window_Size;
  956.       END;
  957.                                    (* Purge receive buffer              *)
  958.    Async_Purge_Buffer;
  959.                                    (* Save Xon/Xoff status              *)
  960.  
  961.    Save_XonXoff     := Async_Do_XonXoff;
  962.    Async_Do_XonXoff := FALSE;
  963.  
  964.                                    (* Do initial handshaking          *)
  965.    Do_Initial_Handshake;
  966.                                    (* If Telink or Ymodem, send the   *)
  967.                                    (* special initial sector, already *)
  968.                                    (* prepared in Send_Modem7_File or *)
  969.                                    (* Send_Ymodem_File                *)
  970.    IF ( NOT Stop_Send ) THEN
  971.       IF ( Transfer_Protocol IN [Ymodem_Batch, Ymodem_G] ) OR
  972.          ( ( Transfer_Protocol IN [Xmodem_1K, Xmodem_1KG] ) AND Use_Ymodem_Header ) THEN
  973.          BEGIN
  974.             Send_Ymodem_Header;
  975.             Use_CRC := TRUE;
  976.             Do_Initial_Handshake;
  977.          END
  978.       ELSE IF ( Transfer_Protocol = Telink ) THEN
  979.          BEGIN
  980.             Send_Telink_Header;
  981.             Use_CRC := TRUE;
  982.          END
  983.       ELSE IF ( Transfer_Protocol = SEALink ) THEN
  984.          Send_SEALink_Header;
  985.  
  986.                                    (* Begin loop over blocks in file    *)
  987.    REPEAT
  988.                                    (* See if Alt-S hit, ending transfer *)
  989.       Check_Keyboard;
  990.  
  991.       Stop_Send := Stop_Send OR ( NOT Async_Carrier_Detect );
  992.  
  993.       IF ( NOT Stop_Send ) THEN
  994.          BEGIN (* Send the next sector *)
  995.  
  996.                                    (* Set block header character        *)
  997.  
  998.             Header_Ch := New_Header_Ch;
  999.  
  1000.                                    (* Read Sector_size chars from file  *)
  1001.                                    (* to be sent.                       *)
  1002.             NRead := Sector_Size;
  1003.  
  1004.             I := Read_File_Handle( XFile_Handle, Sector_Data, NRead );
  1005.  
  1006.                                    (* Check for error *)
  1007.  
  1008.             IF ( I <> 0 ) OR ( Int24Result <> 0 ) THEN
  1009.                BEGIN
  1010.                   Display_Send_Error('Cannot read data from file', TRUE);
  1011.                   Stop_Send := TRUE;
  1012.                END
  1013.                                    (* If no chars. read, then EOF      *)
  1014.  
  1015.             ELSE IF NRead <= 0 THEN
  1016.                EOF_Xfile := TRUE
  1017.             ELSE
  1018.                BEGIN   (* NOT Eof *)
  1019.  
  1020.                                    (* Fill out short sector with 0s     *)
  1021.  
  1022.                   IF ( NRead < Sector_Size ) THEN
  1023.                      FILLCHAR( Sector_Data[NRead+1], Sector_Size - NRead + 1,
  1024.                                0 );
  1025.  
  1026.                                    (* Increment sector number           *)
  1027.  
  1028.                   Sector_Number := SUCC( Sector_Number );
  1029.                   Sector_Count  := Sector_Count  + ( Sector_Size SHR 7 );
  1030.  
  1031.                                    (* Send the block *)
  1032.  
  1033.                   Send_Xmodem_Block;
  1034.  
  1035.                                    (* If Windowing, check if ACK.  If *)
  1036.                                    (* not, backup to offending sector *)
  1037.                                    (* and try again.                  *)
  1038.  
  1039.                   IF ( Do_WXModem OR Do_SeaLink ) THEN
  1040.                      IF ( Ch = NAK ) THEN
  1041.                         BEGIN
  1042.                            Sector_Number := Sector_Number - ACK_Window;
  1043.                            L             := ( ACK_Window + 1 ) * Sector_Size;
  1044.                            I := Relative_Position_File_Handle( XFile_Handle, -L );
  1045.                            EOF_XFile  := FALSE;
  1046.                            XFile_Size := XFile_Size + L;
  1047.                            GOTO 1;
  1048.                         END;
  1049.                                    (* Update transmit time and counts *)
  1050.                                    (* of good/bad sectors; also shift *)
  1051.                                    (* to 128 byte sectors in Ymodem   *)
  1052.                                    (* if ratio of bad/good > 1/6 or   *)
  1053.                                    (* less than 1024 bytes left.      *)
  1054.  
  1055.                   IF Ch = ACK THEN
  1056.                      BEGIN
  1057.                         Time_To_Send := Time_To_Send -
  1058.                                         ( Time_Per_Blk * ( Sector_Size SHR 7 ) );
  1059.                         IF Time_To_Send < 0.0 THEN Time_To_Send := 0.0;
  1060.                         Good_Sectors := SUCC( Good_Sectors );
  1061.                      END
  1062.                   ELSE
  1063.                      BEGIN
  1064.                         Bad_Sectors := SUCC( Bad_Sectors );
  1065.                         IF ( Bad_Threshhold * Bad_Sectors > Good_Sectors ) AND
  1066.                            ( Downsize_Ymodem ) AND ( Sector_Size = 1024 ) THEN
  1067.                            BEGIN
  1068.                               New_Header_Ch := CHR( SOH );
  1069.                               Sector_Size   := 128;
  1070.                               Display_Send_Error('Switching to 128 byte blocks',
  1071.                                                  FALSE);
  1072.                            END;
  1073.                      END;
  1074.                                    (* Alter sector size if Ymodem and *)
  1075.                                    (* less than a Ymodem block left,  *)
  1076.                                    (* and downsizing allowed.         *)
  1077.  
  1078.                   XFile_Size := XFile_Size - NRead;
  1079.  
  1080.                   IF ( ( XFile_Size < 1024.0 ) AND DownSize_Ymodem AND
  1081.                        ( Sector_Size = 1024 ) ) THEN
  1082.                      BEGIN
  1083.                         New_Header_Ch := CHR( SOH );
  1084.                         Sector_Size   := 128;
  1085.                         Display_Send_Error('Switching to 128 byte blocks',
  1086.                                            FALSE);
  1087.                      END;
  1088.  
  1089.                END (* Not EOF *)
  1090.  
  1091.          END (* Send Next Sector *);
  1092.  
  1093.                                    (* Update display *)
  1094. 1:       IF Display_Status THEN
  1095.             BEGIN
  1096.                GoToXY( 26 , 6 );
  1097.                WRITE( TimeString( Time_To_Send , Military_Time ) );
  1098.             END;
  1099.  
  1100.    UNTIL ( EOF_Xfile ) OR ( Tries = Max_Tries ) OR ( Ch = CAN ) OR
  1101.          ( Stop_Send );
  1102.                                    (* If Windowing, wait for final  *)
  1103.                                    (* ACKs to show up.              *)
  1104.  
  1105.    IF ( Do_WXModem OR Do_SeaLink ) THEN
  1106.       BEGIN
  1107.          Max_ACK_Window := 0;
  1108.          WHILE( ( ACK_Window > 0 ) AND ( Ch <> CAN ) AND ( Ch <> TimeOut ) ) DO
  1109.             Handle_Sector_ACKNAK( Ch );
  1110.       END;
  1111.  
  1112.                                    (* Send CANs to host to cancel *)
  1113.                                    (* transfer                    *)
  1114.    IF Stop_Send THEN
  1115.       IF Async_Carrier_Detect THEN
  1116.          Cancel_Transfer;
  1117.  
  1118.    IF Tries >= Max_Tries THEN   (* We failed to send a sector correctly *)
  1119.       Display_Send_Error('No ACK ever received.' , FALSE)
  1120.    ELSE IF ( Ch = CAN ) THEN   (* Receiver cancelled transmission *)
  1121.       Display_Send_Error('Receiver cancelled transmission.',FALSE)
  1122.    ELSE IF Alt_S_Found  THEN (* User cancelled transmission *)
  1123.       Display_Send_Error('Alt-S hit, send cancelled.',FALSE)
  1124.    ELSE IF ( NOT Stop_Send ) THEN  (* We sent everything, try sending EOT *)
  1125.       BEGIN
  1126.  
  1127.          IF ( NOT Display_Status ) THEN
  1128.             Flip_Display_Status;
  1129.                                    (* Wait for output buffer to drain *)
  1130.                                    (* if not doing ACKs               *)
  1131.          IF ( NOT Do_Acks ) THEN
  1132.             BEGIN
  1133.                GoToXY( 26 , 8 );
  1134.                WRITE('Waiting for output buffer to drain');
  1135.                ClrEol;
  1136.                WHILE ( ( Async_OBuffer_Used > 128 ) AND ( NOT Stop_Send ) ) DO
  1137.                   BEGIN
  1138.                      Check_Keyboard;
  1139.                      Stop_Send := Stop_Send OR ( NOT Async_Carrier_Detect );
  1140.                      GiveAwayTime( 1 );
  1141.                   END;
  1142.             END;
  1143.                                    (* Now indicate we're waiting for *)
  1144.                                    (* ACK for EOT                    *)
  1145.          GoToXY( 26 , 8 );
  1146.          WRITE('Waiting for ACK of EOT');
  1147.          ClrEol;
  1148.  
  1149.          Tries   := 0;
  1150.          Do_ACKs := TRUE;
  1151.  
  1152.          REPEAT
  1153.  
  1154.             Async_Send( CHR( EOT ) );
  1155.  
  1156.             Tries := SUCC( Tries );
  1157.  
  1158.             Xmodem_Wait_For_Ch( Xmodem_Ack_Wait , Ch );
  1159.  
  1160.             IF Ch = CAN THEN
  1161.                Xmodem_Wait_For_Ch( Xmodem_Ack_Wait , Ch );
  1162.  
  1163.             IF Display_Status THEN
  1164.                BEGIN
  1165.                   IF ( Tries > 1 ) THEN
  1166.                      Send_Errors := SUCC( Send_Errors );
  1167.                   Update_Xmodem_Send_Display;
  1168.                   GoToXY( 26 , 6 );
  1169.                   WRITE( TimeString( Time_To_Send , Military_Time ) );
  1170.                END;
  1171.  
  1172.             Check_Keyboard;
  1173.  
  1174.          UNTIL ( Ch    = ACK       ) OR
  1175.                ( Tries = Max_Tries ) OR
  1176.                ( Ch    = CAN       ) OR
  1177.                Stop_Send;
  1178.  
  1179.          IF ( NOT Display_Status ) THEN
  1180.             Flip_Display_Status;
  1181.  
  1182.          IF Tries = Max_Tries THEN
  1183.             Display_Send_Error('No ACK on EOT (end of transmission)', FALSE)
  1184.          ELSE IF ( Ch = CAN ) THEN
  1185.             Display_Send_Error('Receiver cancelled transmission.' , FALSE)
  1186.          ELSE IF ( Alt_S_Found OR Stop_Send ) THEN
  1187.             Display_Send_Error('Alt-S key hit, send cancelled.',FALSE)
  1188.          ELSE
  1189.             BEGIN
  1190.  
  1191.                GoToXY( 26 , 8 );
  1192.                WRITE('EOT acknowledged, send complete.');
  1193.                ClrEol;
  1194.  
  1195.                End_Time       := TimeOfDay;
  1196.  
  1197.                IF End_Time < Start_Time THEN
  1198.                   End_Time := End_Time + 86400.0;
  1199.  
  1200.                Effective_Rate := End_Time - Start_Time;
  1201.  
  1202.                IF ( Effective_Rate = 0.0 ) THEN
  1203.                   Effective_Rate := 1.0;
  1204.  
  1205.                Effective_Rate := R_File_Size / Effective_Rate;
  1206.  
  1207.                DELAY( One_Second_Delay );
  1208.  
  1209.                GoToXY( 26 , 8 );
  1210.                WRITE('Transfer rate was ',Effective_Rate:6:1,' CPS');
  1211.                ClrEol;
  1212.  
  1213.                Write_Log( 'Send completed.', TRUE, FALSE );
  1214.  
  1215.                STR( Effective_Rate:6:1 , TName );
  1216.                Write_Log('Transfer rate was ' + TName + ' CPS', TRUE, FALSE );
  1217.  
  1218.             END;
  1219.  
  1220.       END;
  1221.  
  1222.    IF ( NOT Display_Status ) THEN
  1223.       Flip_Display_Status;
  1224.  
  1225.    IF Stop_Send THEN
  1226.       IF Async_Carrier_Drop THEN
  1227.          Display_Send_Error('Carrier dropped.' , FALSE );
  1228.  
  1229.                                       (* Close transferred file           *)
  1230.  
  1231.    I := Close_File_Handle( XFile_Handle );
  1232.    I := Int24Result;
  1233.  
  1234.    DELAY( Two_Second_Delay );
  1235.                                    (* Remove XMODEM window             *)
  1236.    Restore_Screen( Saved_Screen );
  1237.  
  1238.    Reset_Global_Colors;
  1239.                                    (* Turn cursor back on *)
  1240.    CursorOn;
  1241.                                    (* Restore XON/XOFF status *)
  1242.  
  1243.    Async_Do_XonXoff := Save_XonXoff;
  1244.  
  1245.                                    (* Restore status line *)
  1246.    IF Do_Status_Line THEN
  1247.       BEGIN
  1248.          Set_Status_Line_Name( Short_Terminal_Name );
  1249.          Write_To_Status_Line( Status_Line_Name, 1 );
  1250.       END;
  1251.  
  1252. END   (* Send_Xmodem_File *);
  1253.